<template>
  <div class="ar" ref="ardom">
    <!-- <canvas class="bjsCanvas" ref="bjsCanvas" width="400" height="300" /> -->
    <router-link class="back" to="/">返回</router-link>
    <!-- <button class="activeAR" @click="activeAR">开始</button>
    <div class="cameraMatrix">变换矩阵{{ matrix }}</div>
    <div class="cameraProjectionMatrix">投影矩阵{{ projectionMatrix }}</div>
    <div class="cameraPosition">相机位置{{ cameraPosition }}</div>
    <div class="cameraTarget">相机朝向{{ cameraTarget }}</div>
    <div class="cameraMaxZ">相机的远裁剪面{{ cameraMaxZ }}</div>
    <div class="hitPosition">拾取位置{{ hitPosition }}</div> -->
    <model-viewer
        v-if="glbUrl"
        style="width: 100%;height: 100%;"
        :src="glbUrl"
        shadow-intensity="1"
        ar
        camera-controls
        touch-action="pan-y"
        alt="A 3D model carousel">
        <!-- poster="./Chair.webp" -->
        <button slot="ar-button" id="ar-button">开始AR</button>
        <!-- <div id="ar-prompt">
            <img src="/hand.png">
        </div> -->
        <!-- <button id="ar-failure">
            AR未跟踪!
        </button> -->
        <router-link class="back" to="/">返回</router-link>
    </model-viewer>
    <!-- <h1>{{ message }}</h1>
    <button @click="activateXR">Start Hello WebXR</button>
    <div v-for="(item, i) in arInfo" :key="i">
      {{ item }}
    </div> -->
  </div>
</template>

<script lang="ts" setup>
import {
  ref,
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
  onErrorCaptured,
  onRenderTracked,
  onRenderTriggered,
} from "vue";
import queryString from "query-string";
import * as BABYLON from "@babylonjs/core";
import "@babylonjs/loaders/glTF/2.0";
import { round } from "lodash-es";
import { Matrix4, Vector3 } from "three";

const bjsCanvas = ref<HTMLCanvasElement | null>(null);
const ardom = ref<HTMLCanvasElement | null>(null);
const matrix = ref("0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0");
const projectionMatrix = ref("0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0");
const hitPosition = ref([0, 0, 0]);
const cameraPosition = ref([0, 0, 0]);
const cameraTarget = ref([0, 0, 0]);
const cameraMaxZ = ref(1);
const glbUrl = ref("");

// 创建相机
let engine: BABYLON.Engine;
let scene: BABYLON.Scene;
let camera: BABYLON.FreeCamera;
let rootMesh: BABYLON.Mesh;
let plane: BABYLON.Mesh;
let box: BABYLON.Mesh;
let cone: BABYLON.Mesh;
const tempMatrix = new Matrix4();

// 获取地址后面的参数
function getQueryString(key: string): string {
  const parsedHash = queryString.parse(location.search);
  return parsedHash[key] as string;
}

// 加载js
function loadScript(url: string) {
  return new Promise((resolve, reject) => {
    const script = document.createElement("script");
    script.src = url;
    script.type = "module";
    script.onload = resolve;
    script.onerror = reject;
    document.head.appendChild(script);
  });
}

// 转置矩阵
function transposeMatrix(matrix: Float32Array) {
  const result: [
    number,
    number,
    number,
    number,
    number,
    number,
    number,
    number,
    number,
    number,
    number,
    number,
    number,
    number,
    number,
    number
  ] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
  for (let i = 0; i < 4; i++) {
    for (let j = 0; j < 4; j++) {
      result[i * 4 + j] = matrix[j * 4 + i];
    }
  }
  return result;
}

async function activeAR() {
  // 唤起webxr
  if (import.meta.client) {
    // 挂载完成之后被调用：el 被新创建的 vm.$el 替换，并挂载到实例上去之后调用该钩子。
    const canvas = document.createElement("canvas");
    // document.body.appendChild(canvas);
    const gl = canvas.getContext("webgl", { xrCompatible: true });
    // To be continued in upcoming steps.
    // Initialize a WebXR session using "immersive-ar".
    console.log(`xr对象 : ${Boolean(navigator.xr)}`);
    console.log(`xr-gl : ${Boolean(gl)}`);

    if (!navigator.xr || !gl) {
      return;
    }
    const supported = await navigator.xr.isSessionSupported("immersive-ar");

    if (!supported) {
      console.log("AR不支持");
      return;
    } else {
      console.log("AR支持");
    }
    const session = await navigator.xr.requestSession("immersive-ar", {
      requiredFeatures: ["hit-test", "dom-overlay"],
      optionalFeatures: ['dom-overlay', 'light-estimation'],
      domOverlay: { root: document.body },
    });
    if (!session) {
      console.log(`没有,session`);
      return;
    } else {
      console.log(`有,session`);
    }

    session.updateRenderState({
      baseLayer: new XRWebGLLayer(session, gl),
    });

    //“本地”参考空间具有位于以下位置的本地原点
    //在创建会话时靠近观众的位置。
    const referenceSpace = await session.requestReferenceSpace("local");
    //创建另一个以查看器为原点的XRReferenceSpace。
    const viewerSpace = await session.requestReferenceSpace("viewer");
    //使用查看器作为源进行命中测试。
    //@ts-ignore
    const hitTestSource = await session.requestHitTestSource({
      space: viewerSpace,
    });

    let hasPlaneAlert = true;

    //创建一个渲染循环，允许我们在AR视图上绘制。
    const onXRFrame = (time: any, frame: any) => {
      // Queue up the next draw request.
      session.requestAnimationFrame(onXRFrame);

      //将图形帧缓冲区绑定到baseLayer的帧缓冲区
      //   gl.bindFramebuffer(
      //     gl.FRAMEBUFFER,
      //     session.renderState.baseLayer.framebuffer
      //   );

      //检索设备的姿态。
      //当会话尝试建立跟踪时，XRFrame.getViewerPose可以返回null。
      const pose = frame.getViewerPose(referenceSpace);

      if (pose) {
        // 在移动AR中，我们只有一个视图。
        const view = pose.views[0];
        //@ts-ignore
        const viewport = session.renderState.baseLayer.getViewport(view);
        matrix.value = view.transform.matrix
          .toString()
          .replace(/(\.\d{3})\d+/g, "$1");
        projectionMatrix.value = view.projectionMatrix
          .toString()
          .replace(/(\.\d{3})\d+/g, "$1");

        //@ts-ignore
        const viewMatrixBabylon = new BABYLON.Matrix().copyFromFloats(
          //@ts-ignore
          ...transposeMatrix(view.transform.matrix)
        );
        //@ts-ignore
        const projectionMatrixBabylon = new BABYLON.Matrix().copyFromFloats(
          //@ts-ignore
          ...transposeMatrix(view.projectionMatrix)
        );

        // console.log(view.transform.matrix);
        // console.log(view.projectionMatrix);
        // renderer.setSize(viewport.width, viewport.height);
        //   if (viewport) {
        //     engine.setSize(viewport.width, viewport.height);
        //   }

        //使用视图的变换矩阵和投影矩阵来配置THREE.摄影机。

        // camera._projectionMatrix = projectionMatrixBabylon;

        // const viewMatrix = view.transform.inverse.matrix;
        // const viewMatrix = view.transform.matrix;

        // const position = new BABYLON.Vector3().set(
        //   viewMatrix[12],
        //   viewMatrix[13],
        //   viewMatrix[14]
        // );
        // const forward = new BABYLON.Vector3()
        //   .set(viewMatrix[8], viewMatrix[9], viewMatrix[10])
        //   .normalize();
        // const up = new BABYLON.Vector3()
        //   .set(viewMatrix[4], viewMatrix[5], viewMatrix[6])
        //   .normalize();

        // camera.position.set(position.x,position.y,position.z);
        // camera.setTarget(target);
        // camera.setTarget(camera.position.add(forward));

        // cone.position.set(-position.x,position.y,position.z);
        // cone.lookAt(target);

        // 假设你已经获取了 WebXR 的视图矩阵
        const viewMatrix = view.transform.inverse.matrix; // 从 WebXR 获取视图矩阵

        // 将 WebXR 的视图矩阵转换为 Babylon.js 的矩阵格式
        const babylonMatrix = new BABYLON.Matrix();
        babylonMatrix.set(
          viewMatrix[0],
          viewMatrix[1],
          viewMatrix[2],
          viewMatrix[3],
          viewMatrix[4],
          viewMatrix[5],
          viewMatrix[6],
          viewMatrix[7],
          viewMatrix[8],
          viewMatrix[9],
          viewMatrix[10],
          viewMatrix[11],
          viewMatrix[12],
          viewMatrix[13],
          viewMatrix[14],
          viewMatrix[15]
        );

        // 提取位置和旋转
        const position = new BABYLON.Vector3().set(
          babylonMatrix.m[12],
          babylonMatrix.m[13],
          babylonMatrix.m[14]
        );
        const quaternion = new BABYLON.Quaternion().fromRotationMatrix(
          babylonMatrix
        );

        // 应用到相机
        camera.position.copyFrom(position);
        camera.rotationQuaternion = quaternion;

        cameraPosition.value = [
          camera.position.x,
          camera.position.y,
          camera.position.z,
        ].map((v) => round(v, 4));
        cameraTarget.value = [
          camera.target.x,
          camera.target.y,
          camera.target.z,
        ].map((v) => round(v, 4));

        cameraMaxZ.value = camera.maxZ;

        // camera.upVector = up;

        // camera.updateMatrixWorld(true);

        const hitTestResults = frame.getHitTestResults(hitTestSource);
        if (hitTestResults.length > 0) {
          if (hasPlaneAlert) {
            console.log("识别到了平面");
            hasPlaneAlert = false;
          }
          const hitPose = hitTestResults[0].getPose(referenceSpace);

          // (window.camera as BABYLON.FreeCamera).viewMatrix
          // camera.viewMatrix = viewMatrix;
          // camera.projectionMatrix = projectionMatrix;
          //   reticle.visible = true;
          //   reticle.position.set(
          //     hitPose.transform.position.x,
          //     hitPose.transform.position.y,
          //     hitPose.transform.position.z
          //   );
          const x = round(hitPose.transform.position.x, 4);
          const y = round(hitPose.transform.position.y, 4);
          const z = round(hitPose.transform.position.z, 4);
          hitPosition.value = [x, y, z];
          if (plane) {
            const [x, y, z] = hitPosition.value;
            plane.position.set(x, y, z);
          }
          //   reticle.updateMatrixWorld(true);
        }
      }

      // Render the scene with THREE.WebGLRenderer.
      // renderer.render(scene, camera);
      //   scene.render();
    };
    session.requestAnimationFrame(onXRFrame);
    // 锚点被确认时,在这个位置放上一个模型
    session.addEventListener("select", (event) => {
      console.log("点击了地面");
      if (rootMesh) {
        const [x, y, z] = hitPosition.value;
        rootMesh.position.set(x, y, z);
      }
    });
  }
}

onMounted(async () => {
  if (import.meta.client) {
    import("vconsole").then(({ default: Vconsole }) => {
      if (ardom.value) {
        new Vconsole({
          target: ardom.value,
        });
      }
    });
  }

  const url: string = getQueryString("url");
  if(url){
      glbUrl.value = url;
      await loadScript("/model-viewer.js");
  }else{
     location.href = "/";
     return;
  }

  if (import.meta.client && bjsCanvas.value) {
    engine = new BABYLON.Engine(bjsCanvas.value, true);
    scene = new BABYLON.Scene(engine);
    scene.clearColor = new BABYLON.Color4(0, 0, 0, 0);

    // 创建相机
    camera = window.camera = new BABYLON.FreeCamera(
      "camera",
      BABYLON.Vector3.Zero(),
      scene
    );
    camera.minZ = 0.001;
    camera.maxZ = 10000;

    camera.attachControl(bjsCanvas.value, true);

    // 创建灯光
    // const light = new BABYLON.HemisphericLight(
    //   "light",
    //   BABYLON.Vector3.Up(),
    //   scene
    // );
    // light.intensity = 2;

    //创建点光源
    const pointLight = new BABYLON.PointLight(
      "pointLight",
      new BABYLON.Vector3(3, 3, 3),
      scene
    );
    pointLight.intensity = 1;

    //创建点光源
    const pointLight2 = new BABYLON.PointLight(
      "pointLight",
      new BABYLON.Vector3(-3, 3, -3),
      scene
    );
    pointLight2.intensity = 1;

    const url: string = getQueryString("url");
    glbUrl.value = url;
    if (url) {
      const { meshes, particleSystems, skeletons } =
        await BABYLON.SceneLoader.ImportMeshAsync(
          "", // 不加载特定的网格，加载整个文件
          url, // GLB 文件的路径
          "",
          scene
        );
      //@ts-ignore
      rootMesh = meshes[0];
      rootMesh.scaling.set(0.1, 0.1, 0.1);
      scene.render();
    } else {
      location.href = "/";
    }

    //创建一个立方体
    // box = BABYLON.MeshBuilder.CreateBox("box", { size: 0.2 }, scene);

    // 创建一个四边形的圆锥
    // cone = new BABYLON.Mesh("cone", scene);

    // // 定义四边形的顶点
    // const positions = [
    //   // 底面四个顶点
    //   -0.2,
    //   -0.2,
    //   0, // 左下
    //   0.2,
    //   -0.2,
    //   0, // 右下
    //   0.2,
    //   0.2,
    //   0, // 右上
    //   -0.2,
    //   0.2,
    //   0, // 左上
    //   0,
    //   0,
    //   0.4, // 顶点
    // ];

    // // 定义顶点的索引（定义三角形的连接方式）
    // const indices = [
    //   // 左下三角形
    //   0, 1, 4,
    //   // 右下三角形
    //   1, 2, 4,
    //   // 右上三角形
    //   2, 3, 4,
    //   // 左上三角形
    //   3, 0, 4,
    // ];

    // // 创建顶点数据
    // const vertexData = new BABYLON.VertexData();
    // vertexData.positions = positions;
    // vertexData.indices = indices;
    // // 应用顶点数据到网格
    // vertexData.applyToMesh(cone);
    // // 添加材质
    // const material = new BABYLON.StandardMaterial("material", scene);
    // material.backFaceCulling = false; // 设置双面显示
    // cone.material = material;

    // cone.position.set(1, 1, 1);
    // cone.lookAt(new BABYLON.Vector3(0, 0, 0));

    // 创建一个球形网格 材质是网格显示
    const sphere = BABYLON.MeshBuilder.CreateSphere(
      "sphere",
      { diameter: 1, segments: 5 },
      scene
    );
    //设置一个材质
    const sphereMaterial = new BABYLON.StandardMaterial(
      "sphereMaterial",
      scene
    );
    sphereMaterial.diffuseColor = new BABYLON.Color3(1, 0, 0);
    sphereMaterial.wireframe = true;
    sphere.material = sphereMaterial;

    //创建一个小方片,躺在地上的
    plane = BABYLON.MeshBuilder.CreatePlane(
      "plane",
      { width: 0.1, height: 0.1 },
      scene
    );
    plane.position.set(0, 0, 0);
    plane.rotation.x = Math.PI / 2;
    const planeMaterial = new BABYLON.StandardMaterial("sphereMaterial", scene);
    planeMaterial.diffuseColor = new BABYLON.Color3(0, 1, 0);
    // planeMaterial.wireframe = true;
    plane.material = planeMaterial;

    // 三维轴线
    import("../component3d/Axis/Axis").then(({ default: Axis }) => {
      const axis = new Axis(10, scene);
    });

    // const env = scene.createDefaultEnvironment();
    // if (env) {
    //   const xr = await scene.createDefaultXRExperienceAsync({
    //     floorMeshes: [env.ground!],
    //   });
    // }

    // // 初始化 WebXR 功能
    // const xrHelper = await scene.createDefaultXRExperienceAsync({
    //   uiOptions: {
    //     sessionMode: "immersive-vr",
    //   },
    //   optionalFeatures: ["hit-test", "anchors"],
    // });

    // // 启用平面检测
    // const baseExperience = xrHelper.baseExperience;
    // console.log("baseExperience", baseExperience);

    // const planeDetector = xrHelper.baseExperience.featuresManager.enableFeature(
    //   BABYLON.WebXRFeatureName.PLANE_DETECTION,
    //   "latest"
    // ) as BABYLON.WebXRPlaneDetector;

    // // 平面检测事件处理
    // planeDetector.onPlaneAddedObservable.add((plane: BABYLON.IWebXRPlane) => {
    //   // 当检测到平面时，将立方体放置在平面上
    //   const position = plane.polygonDefinition
    //     .reduce((sum, point) => sum.add(point), BABYLON.Vector3.Zero())
    //     .scale(1 / plane.polygonDefinition.length);
    //   box.position = position;
    //   box.position.y = 0.1; // 确保立方体放置在平面上
    // });

    engine.runRenderLoop(() => {
      scene.render();
    });

    window.addEventListener("resize", () => {
      engine.resize();
    });

    // await loadScript(
    //   "https://unpkg.com/webxr-polyfill@latest/build/webxr-polyfill.module.js"
    // );
  }
});

onBeforeUpdate(() => {
  // 数据更新时调用，发生在虚拟 DOM 重新渲染之前。
});

onUpdated(() => {
  // 由于数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用该钩子。
});

onBeforeUnmount(() => {
  // 实例销毁之前调用。在这一步，实例仍然完全可用。
});

onUnmounted(() => {
  // 实例销毁后调用。此时实例已经完全销毁。
});

onErrorCaptured((err: Error, vm, info: string) => {
  // 当捕获一个来自后代组件的错误时被调用。
  return true; // 返回 true 可以阻止错误继续向上冒泡
});

onRenderTracked((event) => {
  // 跟踪虚拟 DOM 重新渲染时被调用。
});

onRenderTriggered((event) => {
  // 触发虚拟 DOM 重新渲染时被调用。
});
</script>

<style lang="less" scoped>
.ar {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  .back {
    position: absolute;
    top: 10px;
    left: 10px;
    font-size: 20px;
    color: #fff;
    background-color: #000000;
    padding: 2px 14px;
    border-radius: 20px;
    z-index: 10000;
  }

  #ar-button{
    padding: 0px 40px 0px 40px;
    position: absolute;
    bottom: 180px;
    left: 50%;
    text-align: center;
    transform: translate(-50%, 0%);
    font-size: 14px;
    z-index: 10000;
  }

  .bjsCanvas {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 10000;
  }

  .babylonjsAR {
    position: absolute;
    bottom: 220px;
    left: 50%;
    text-align: center;
    transform: translate(-50%, 0%);
    font-size: 14px;
    z-index: 10000;
  }

  .activeAR {
    position: absolute;
    bottom: 180px;
    left: 50%;
    text-align: center;
    transform: translate(-50%, 0%);
    font-size: 14px;
    z-index: 10000;
  }

  .cameraMatrix {
    position: absolute;
    width: 100%;
    bottom: 150px;
    left: 50%;
    text-align: center;
    transform: translate(-50%, 0%);
    font-size: 14px;
    z-index: 10000;
    color: #fff;
    text-shadow: 1px 1px 1px #000;
  }
  .cameraProjectionMatrix {
    .cameraMatrix;
    bottom: 120px;
  }
  .cameraPosition {
    .cameraMatrix;
    bottom: 100px;
  }
  .cameraTarget {
    .cameraMatrix;
    bottom: 80px;
  }
  .hitPosition {
    .cameraMatrix;
    bottom: 60px;
  }
  .cameraMaxZ {
    .cameraMatrix;
    bottom: 220px;
  }
}
</style>
